package progma.lang.compiler;

import static progma.lang.compiler.VarType.QUALIFIED_ID;
import static progma.lang.compiler.VarType.VARI;

import java.util.HashMap;
import java.util.regex.Pattern;

import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.RecognizerSharedState;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenStream;
import org.antlr.runtime.debug.DebugEventListener;
import org.antlr.runtime.debug.DebugParser;

import progma.lang.compiler.exception.ClassNameException;
import progma.lang.compiler.exception.InvalidFixityException;
import progma.lang.compiler.exception.WrongVarKindException;

public class DebugProgmaBaseParser extends DebugParser {

	// fixity table
	protected HashMap<String, Integer> fixities = new HashMap<String, Integer>();
	
	public DebugProgmaBaseParser(TokenStream input) {
		this(input, new RecognizerSharedState());
	}

	public DebugProgmaBaseParser(TokenStream input, RecognizerSharedState state) {
		super(input, state);
		// default fixity
		fixities.put("==", 2);
		fixities.put("!=", 2);
		fixities.put(">", 3);
		fixities.put("<", 3);
		fixities.put(">=", 3);
		fixities.put("<=", 3);
		fixities.put("+", 4);
		fixities.put("-", 4);
		fixities.put("*", 5);
		fixities.put("/", 5);
		fixities.put("%", 5);
	}
	
	public DebugProgmaBaseParser(TokenStream input, DebugEventListener dbg) {
        super(input, dbg);
     // default fixity
		fixities.put("==", 2);
		fixities.put("!=", 2);
		fixities.put(">", 3);
		fixities.put("<", 3);
		fixities.put(">=", 3);
		fixities.put("<=", 3);
		fixities.put("+", 4);
		fixities.put("-", 4);
		fixities.put("*", 5);
		fixities.put("/", 5);
		fixities.put("%", 5);
    } 
	
	///////////////////////////////////////////////////////////////////////
	
	public HashMap<String, Integer> getFixities() {
		return fixities;
	}
	
	@Override
	public String getErrorMessage(RecognitionException re, String[] tokenNames) {
		if (re instanceof WrongVarKindException) {
			WrongVarKindException ex = (WrongVarKindException) re;
			String msg = null;
			switch(ex.expected) {
				case QUALIFIED_ID:
					msg = "identifiers can't contain special characters";
					break;
				case VARI:
					msg = "expecting: simple variable, found: fully qualified variable or operator";
					break;
			}
			return msg;
		} else if(re instanceof InvalidFixityException) {
			InvalidFixityException ex = (InvalidFixityException) re;
			String msg = "invalid fixity declaration, fixity number should be between 0 and 9, found: " +
				ex.found;
			return msg;
		}
		return super.getErrorMessage(re, tokenNames);
	}
	
	protected void checkVar(Token var, VarType type) throws WrongVarKindException {
		switch(type) {
			case QUALIFIED_ID:
				if(!isQualifiedId(var)) {
					//input.rewind();
					throw new WrongVarKindException(input, QUALIFIED_ID);
				}
				break;
			case VARI:
				if(!isVar(var)) {
					//input.rewind();
					throw new WrongVarKindException(input, VARI);
				}
				break;
		}
	}
	
	protected boolean checkFixity(Token op, int fixity) {
		if(fixity == -1) // don't want to check
			return true;
		
		if(fixities.containsKey(op.getText()))
			return fixities.get(op.getText()) == fixity;
		return 9 == fixity; // max precedence if not defined
	}
	
	private final Pattern fixityPattern = Pattern.compile("\\d");
	protected void checkValidFixityDef(Token number) throws InvalidFixityException {
		if(!fixityPattern.matcher(number.getText()).matches())
			throw new InvalidFixityException(input, number.getText());
	}
	
	private final Pattern namePattern = Pattern.compile("[_|\\d|a-z|A-Z]+");
	protected void checkClassName(Token var) throws ClassNameException {
		if(!namePattern.matcher(var.getText()).matches())
			throw new ClassNameException(input, var.getText());
	}
	
	protected void addFixity(Token number, Token op) {
		fixities.put(op.getText(), Integer.parseInt(number.getText()));
	}
	
	private final Pattern idPattern = Pattern
		.compile("[a-z|A-Z|_][a-z|A-Z|_|0-9]*");
	protected boolean isId(String var) {
		return idPattern.matcher(var).matches();
	}
	
	protected boolean isId(Token tk) {
		return isId(tk.getText());
	}
	
	protected boolean isQualifiedId(String var) {
		String[] frags = var.split("/");

		for (int i = 0; i < frags.length; i++) {
			if (!isId(frags[i]))
				return false;
		}
		return true;
	}
	
	protected boolean isQualifiedId(Token tk) {
		return isQualifiedId(tk.getText());
	}
	
	protected boolean isVar(String var) {
		return !var.contains("/");
	}
	
	protected boolean isVar(Token tk) {
		return isVar(tk.getText());
	}
}
